package com.luoke.service.impl;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.luoke.constant.ModuleTypeConstant;
import com.luoke.constant.StatusConstant;
import com.luoke.constant.SubmoduleTypeConstant;
import com.luoke.context.GlobalDataStore;
import com.luoke.dao.*;
import com.luoke.dao.impl.*;
import com.luoke.pojo.dto.BagDTO;
import com.luoke.pojo.dto.UserDTO;
import com.luoke.pojo.entity.*;
import com.luoke.result.Result;
import com.luoke.service.UserResponseService;
import com.luoke.util.myjdbc.QueryWrapper;
import com.luoke.util.myjdbc.SQLEnum;
import com.luoke.util.myjdbc.UpdateWrapper;
import lombok.extern.slf4j.Slf4j;
import org.java_websocket.WebSocket;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 对用户请求响应的服务层
 */
@Slf4j
public class UserResponseServiceImpl implements UserResponseService {

    private final UserDao userDao = new UserDaoImpl();
    private final BagDao bagDao = new BagDaoImpl();
    private final PetDao petDao = new PetDaoImpl();
    private final UserPetDao userPetDao = new UserPetDaoImpl();
    private final AttributeDao attributeDao = new AttributeDaoImpl();
    private final SkillDao skillDao = new SkillDaoImpl();
    private final PetSkillDao petsSkillDao = new PetSkillDaoImpl();
    private final OrderDao orderDao = new OrderDaoImpl();
    private final BagMapper bagMapper = new BagMapperImpl();
    private final MapDao mapDao = new MapDaoImpl();
    private final MapPetDao mapPetDao = new MapPetDaoImpl();
    private final GoodsDao goodsDao = new GoodsDaoImpl();

    /**
     * 对客户端发来的登录请求做相应的处理后返回给客户端
     *
     * @param conn   与客户端的通信对象
     * @param result 客户端数据
     */
    @Override
    public void userLogin(WebSocket conn, Result result) {
        result.setData(JSONUtil.toBean(result.getData().toString(), UserDTO.class));
        UserDTO data = (UserDTO) result.getData();

        User user = userDao.getUserByNameAndPwd(data.getUsername(), data.getPassword());
        System.out.println(user);
        if (user != null) {
            Result<User> response = new Result<>();
            List<Pet> pet = getPets(user);
            List<BagDTO> bag = getBag(data.getUsername());
            response.setModuleName(ModuleTypeConstant.USER);
            response.setCommand(SubmoduleTypeConstant.USER_LOGIN); //这里还需要设置子模块的名称
            response.setStatus("success");
            response.setMessage("登录成功");
            response.setCurrentPet(pet);
            response.setCurrentBag(bag);
            response.setData(user);
            //更新数据库中最后一次登录时间
            userDao.UpdateLastTimeByName(data.getUsername());
            //这里还需要把用户的在线状态设置为true
            userDao.updateOnlineStatus(data.getUsername(), true);
            //还需要保存在线用户的用户名和对应的 WebSocket 连接   方便后续进行匹配时的通信
            GlobalDataStore.onlineUser.put(data.getUsername(), conn);
            conn.send(JSONUtil.toJsonStr(response)); //把成功的消息响应给客户端
            System.out.println(data.getUsername() + " 登录成功");
        } else {
            Result response = new Result<>();
            response.setModuleName(ModuleTypeConstant.USER);
            response.setCommand(SubmoduleTypeConstant.USER_LOGIN); //这里还需要设置子模块的名称
            response.setStatus("fail");
            response.setMessage("登录失败,账号或密码错误");
            conn.send(JSONUtil.toJsonStr(response)); //把失败的消息响应给客户端
        }
    }


    /**
     * 对客户端发来的注册请求做相应的处理后返回给客户端
     *
     * @param conn   与客户端的通信对象
     * @param result 客户端数据
     */
    @Override
    public void userRegister(WebSocket conn, Result result) {
        result.setData(JSONUtil.toBean(result.getData().toString(), UserDTO.class));
        UserDTO data = (UserDTO) result.getData();

        if (userDao.getUserByName(data.getUsername()) != null) {
            Result response = new Result<>();
            response.setModuleName(ModuleTypeConstant.USER);
            response.setCommand(SubmoduleTypeConstant.USER_REGISTER); //这里还需要设置子模块的名称
            response.setStatus("fail");
            response.setMessage("注册失败,用户名已存在");
            conn.send(JSONUtil.toJsonStr(response)); //把失败的消息响应给客户端
        } else {
            User user = new User();
            user.setName(data.getUsername());
            user.setPassword(data.getPassword());
            user.setStatus(1);//设置账户未封禁
            user.setCreateTime(LocalDateTime.now()); //设置账户创建时间
            user.setMoney(2000); //设置金钱为2000
            user.setExp(0); //设置经验值为0
            user.setVip(0);//设置vip等级为0
            Long key = userDao.saveUser(user);
            //送一只默认宠物
            UserPet userPet = new UserPet();
            userPet.setUserId(key.intValue());
            userPet.setPetId(16);
            userPet.setPosition(1);
            userPetDao.insert(userPet);
            //送三个小洛克球三个小药瓶三个小蓝瓶
            Bag bag1 = new Bag(null,key.intValue(),3L,3,null,null);
            Bag bag2 = new Bag(null,key.intValue(),2L,3,null,null);
            Bag bag3 = new Bag(null,key.intValue(),1L,3,null,null);
            bagDao.insert(bag1);bagDao.insert(bag2);bagDao.insert(bag3);
            Result response = new Result<>();
            response.setModuleName(ModuleTypeConstant.USER);
            response.setCommand(SubmoduleTypeConstant.USER_REGISTER); //这里还需要设置子模块的名称
            response.setStatus("success");
            response.setMessage("用户 '" + data.getUsername() + "' 注册成功");
            conn.send(JSONUtil.toJsonStr(response)); //把成功的消息响应给客户端
        }


    }

    /**
     * 对客户端发来的注销请求做相应的处理后返回给客户端
     *
     * @param conn   与客户端的通信对象
     * @param result 客户端数据
     */
    @Override
    public void userLogout(WebSocket conn, Result result) {
        userDao.updateOnlineStatus(result.getData().toString(), false);
        Result response = new Result();
        response.setModuleName(ModuleTypeConstant.USER);
        response.setCommand(SubmoduleTypeConstant.USER_LOGOUT); //这里还需要设置子模块的名称
        response.setStatus("success");
        response.setMessage("用户 '" + result.getData().toString() + "' 注销成功");
        conn.send(JSONUtil.toJsonStr(response)); //把成功的消息响应给客户端
        GlobalDataStore.onlineUser.remove(result.getData().toString());
        System.out.println(result.getData().toString() + " 下线了");
    }

    /**
     * 根据玩家名字获取背包信息
     *
     * @param conn   与客户端的通信对象
     * @param result 客户端数据
     */
    @Override
    public void getBag(WebSocket conn, Result result) {

        //这里应该根据用户id查询背包中的物品
        String data = (String) result.getData();
        List<BagDTO> bags = getBag(data);

//        QueryWrapper<Goods> qw = new QueryWrapper<>();
//        qw.select(SQLEnum.ASTERISK_SIGN.getValue())
//                .from(Goods.class);
//        List<Goods> goods = goodsDao.selectAll(qw);

        Result response = new Result();
        response.setModuleName(ModuleTypeConstant.USER);
        response.setCommand(SubmoduleTypeConstant.BAG); //这里还需要设置子模块的名称
        response.setStatus("success");
        response.setData(bags);
        conn.send(JSONUtil.toJsonStr(response)); //把成功的消息响应给客户端
        System.out.println(result.getData() + "获取背包信息");
    }

    private List<BagDTO> getBag(String name) {
        return bagDao.getUserBagByUsername(name);
    }

    /**
     * 根据玩家name获取宠物的列表
     *
     * @param conn   与客户端的通信对象
     * @param result 客户端数据
     */
    @Override
    public void getPets(WebSocket conn, Result result) {
        User data = JSONUtil.toBean(result.getData().toString(), User.class);
        List<Pet> pet = getPets(data);

        Result response = new Result();
        response.setModuleName(ModuleTypeConstant.USER);
        response.setCommand(SubmoduleTypeConstant.PETS); //这里还需要设置子模块的名称
        response.setStatus("success");
        response.setData(pet);
        conn.send(JSONUtil.toJsonStr(response)); //把成功的消息响应给客户端
        System.out.println(data.getName() + "获取宠物");
    }

    private List<Pet> getPets(User data) {
        //首先查询用户宠物表的所有数据
        QueryWrapper<UserPet> userPetWrapper = new QueryWrapper<>();
        userPetWrapper.select(SQLEnum.ASTERISK_SIGN.getValue())
                .from(UserPet.class)
                .where("user_id", data.getId().toString());
        List<UserPet> userPets = userPetDao.selectAll(userPetWrapper);
        //根据用户过滤数据
        List<Integer> petIds = userPets.stream().map(UserPet::getPetId).collect(Collectors.toList());
        //然后查询所有的宠物表
        QueryWrapper<Pet> qw = new QueryWrapper<>();
        qw.select(SQLEnum.ASTERISK_SIGN.getValue())
                .from(Pet.class);
        List<Pet> pet = petDao.selectAll(qw);
        //通过petIds 过滤出用户所拥有的宠物信息
        pet = pet.stream().filter(item -> petIds.contains(item.getId().intValue())).collect(Collectors.toList());

        // 创建 UserPet 的映射，方便根据 petId 找到对应的等级
        java.util.Map<Integer, Integer> petLevelMap = userPets.stream().collect(Collectors.toMap(UserPet::getPetId, UserPet::getLevel));
        // 设置宠物等级
        pet.forEach(pets -> {
            Integer level = petLevelMap.get(pets.getId().intValue());
            if (level != null) {
                pets.setLevel(level);
            }
        });

        //填充宠物技能
        fillSkill(pet);
        return pet;
    }

    /**
     * 填充所有的宠物属性
     *
     * @param pet 玩家对应的宠物数据
     */
    private void fillSkill(List<Pet> pet) {
        for (Pet item : pet) {
            QueryWrapper<Attribute> attrWrapper = new QueryWrapper<>();
            attrWrapper.select(SQLEnum.ASTERISK_SIGN.getValue())
                    .from(Attribute.class)
                    .where("id", item.getAttributeId().toString());
            Attribute attribute = attributeDao.selectOne(attrWrapper);
            item.setAttribute(attribute);
        }

        querySkill(pet);
    }

    /**
     * 获取商店的内容
     *
     * @param conn   与客户端的通信对象
     * @param result 客户端数据
     */
    @Override
    public void getShow(WebSocket conn, Result result) {
        QueryWrapper<Goods> query = new QueryWrapper<>();
        query.select(SQLEnum.ASTERISK_SIGN.getValue())
                .from(Goods.class);
        List<Goods> goods = goodsDao.selectAll(query);
        Result response = new Result();
        response.setModuleName(ModuleTypeConstant.USER);
        response.setCommand(SubmoduleTypeConstant.SHOP); //这里还需要设置子模块的名称
        response.setStatus("success");
        response.setData(goods);
        conn.send(JSONUtil.toJsonStr(response)); //把成功的消息响应给客户端
    }

    /**
     * 对商店中购买物品的请求的处理
     *
     * @param conn   与客户端的通信对象
     * @param result 客户端数据
     */
    @Override
    public void getBuy(WebSocket conn, Result result) {
        HashMap map = JSONUtil.toBean(JSONUtil.toJsonStr(result.getData()), HashMap.class);
        String userStr = (String) map.get("user");
        String good = (String) map.get("goods");
        String quantity = (String) map.get("quantity");
        Goods goods = JSONUtil.toBean(good, Goods.class);
        User user = JSONUtil.toBean(userStr, User.class);
        updateUser(goods, user, quantity);
        insertOrder(goods, user, quantity);
        insertBag(goods, user, quantity);
        Result response = new Result();
        response.setModuleName(ModuleTypeConstant.USER);
        response.setCommand(SubmoduleTypeConstant.BUY); //这里还需要设置子模块的名称
        response.setStatus("success");
        conn.send(JSONUtil.toJsonStr(response)); //把成功的消息响应给客户端
    }

    /**
     * 获取系统信息
     *
     * @param conn   与客户端的通信对象
     * @param result 客户端数据
     */
    @Override
    public void getMap(WebSocket conn, Result result) {
        QueryWrapper<Map> qw = new QueryWrapper<>();
        qw.select(SQLEnum.ASTERISK_SIGN.getValue())
                .from(Map.class);
        List<Map> mapList = mapDao.selectAll(qw);
        queryPet(mapList);
        Result response = new Result();
        response.setModuleName(ModuleTypeConstant.USER);
        response.setCommand(SubmoduleTypeConstant.MAP); //这里还需要设置子模块的名称
        response.setStatus("success");
        response.setData(mapList);
        conn.send(JSONUtil.toJsonStr(response)); //把成功的消息响应给客户端
    }

    /**
     * 对于宠物捕捉的成功的处理
     *
     * @param conn   与客户端的通信对象
     * @param result 客户端数据
     */
    @Override
    public void pve(WebSocket conn, Result result) {
        HashMap map = JSONUtil.toBean(JSONUtil.toJsonStr(result.getData()), HashMap.class);
        String index = (String) map.get("index");
        String id = (String) map.get("id");
        Pet pet = JSONUtil.toBean((String) map.get("pet"), Pet.class);
        JSONArray bagList = JSONUtil.parseArray((String) map.get("bag"));
        insertPet(index, id, pet);
        updateBag(bagList, id);
        Result response = new Result();
        response.setModuleName(ModuleTypeConstant.USER);
        response.setCommand(SubmoduleTypeConstant.PVE);
        response.setStatus(StatusConstant.SUCCESS);
        conn.send(JSONUtil.toJsonStr(response));
    }

    /**
     * 对于小游戏的处理
     *
     * @param conn   与客户端的通信对象
     * @param result 客户端数据
     */
    @Override
    public void miniGame(WebSocket conn, Result result) {
        User user = userDao.selectOne(new QueryWrapper<User>().select(User::getMoney).from(User.class).where(User::getName, result.getFromUser()));

        System.out.println(user.getMoney());

        UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
        userUpdateWrapper.update(User.class)
                .set(User::getMoney, String.valueOf((Integer.parseInt(result.getData().toString()) + user.getMoney())))
                .where(User::getName, result.getFromUser());
        userDao.update(userUpdateWrapper);


    }

    @Override
    public void showUserOnlineName(WebSocket conn, Result result) {
        java.util.Map<String, WebSocket> onlineUser = GlobalDataStore.onlineUser;
        Set<String> strings = onlineUser.keySet();
        StringBuilder names = new StringBuilder();
        for (String str : strings) {
            names.append(str).append(",");
        }
        names.deleteCharAt(names.length() - 1);
        Result response = new Result();
        response.setModuleName(ModuleTypeConstant.USER);
        response.setCommand(SubmoduleTypeConstant.SHOW_USER_ONLINE_NAME);
        response.setStatus(StatusConstant.SUCCESS);
        response.setData(names.toString());

        conn.send(JSONUtil.toJsonStr(response));
    }

    private void updateBag(JSONArray bagList, String id) {
        for (int i = 0; i < bagList.size(); i++) {
            UpdateWrapper<Bag> uw = new UpdateWrapper<>();
            StringBuffer stringBuffer = new StringBuffer();
            BagDTO bag = bagList.get(i, BagDTO.class);
            stringBuffer.append(
                    StrUtil.format("update bag set quantity = {} where user_id = {}", bag.getQuantity(), id));
            log.info("修改背包数量sql:{}", stringBuffer);
            uw.setSql(stringBuffer);
            bagMapper.update(uw);
        }
    }

    private void insertPet(String index, String id, Pet pet) {
        UserPet userPet = new UserPet();
        userPet.setUserId(Integer.parseInt(id));
        userPet.setPetId(Math.toIntExact(pet.getId()));
        userPet.setLevel(1);
        userPet.setHp(100);
        userPet.setMp(100);
        userPet.setPosition(Integer.parseInt(index));
        userPetDao.insert(userPet);
    }

    private void queryPet(List<Map> mapList) {
        for (Map map : mapList) {
            QueryWrapper<MapPet> qw = new QueryWrapper<>();
            qw.select(SQLEnum.ASTERISK_SIGN.getValue())
                    .from(MapPet.class)
                    .where("map_id", map.getId().toString());
            List<MapPet> mapPetList = mapPetDao.selectAll(qw);
            List<Pet> pet = getPet(mapPetList);
            map.setPets(pet);
        }
    }

    private List<Pet> getPet(List<MapPet> mapPetList) {
        List<Pet> petList = new ArrayList<>();
        for (MapPet item : mapPetList) {
            QueryWrapper<Pet> qw = new QueryWrapper<>();
            qw.select(SQLEnum.ASTERISK_SIGN.getValue())
                    .from(Pet.class)
                    .where("id", item.getPetId().toString());
            Pet pet = petDao.selectOne(qw);
            petList.add(pet);
        }
        fillSkill(petList);
        return petList;
    }


    private void insertBag(Goods goods, User user, String quantity) {
        QueryWrapper<Bag> qw = new QueryWrapper<>();
        qw.select(SQLEnum.ASTERISK_SIGN.getValue())
                .from(Bag.class)
                .where("user_id", user.getId().toString())
                .and(Bag::getGoodsId, String.valueOf(goods.getId()));
        Bag bag = bagMapper.selectOne(qw);
        if (bag != null) {
            bag.setQuantity(Integer.valueOf(quantity) + bag.getQuantity());
            bagMapper.updateById(bag);
        } else {
            bag = new Bag();
            bag.setUserId(Math.toIntExact(user.getId()));
//            bag.setGoodsId(Math.toIntExact(goods.getId()));
            bag.setGoodsId(goods.getId());
            bag.setQuantity(Integer.valueOf(quantity));
            bag.setCreatedTime(LocalDateTime.now());
            bagMapper.insert(bag);
        }
    }

    private void insertOrder(Goods goods, User user, String quantity) {
        Order order = new Order();
        order.setGoodsId(Math.toIntExact(goods.getId()));
        order.setUserId(Math.toIntExact(user.getId()));
        order.setQuantity(Integer.valueOf(quantity));
        order.setCreateTime(LocalDateTime.now());
        Integer price = goods.getPrice() * Integer.parseInt(quantity);
        order.setTotalPrice(Double.valueOf(price));
        orderDao.insert(order);
    }

    private void updateUser(Goods goods, User user, String quantity) {
        UpdateWrapper<User> userQueryWrapper = new UpdateWrapper<>();
        String sql = StrUtil.format("update user set money = {} where id = {}",
                user.getMoney(), user.getId());
        StringBuffer sb = new StringBuffer();
        sb.append(sql);
        userQueryWrapper.setSql(sb);
        userDao.update(userQueryWrapper);
    }

    /**
     * 查询所有的宠物技能
     *
     * @param pet
     */
    private void querySkill(List<Pet> pet) {
        for (Pet item : pet) {
            QueryWrapper<PetSkill> qw = new QueryWrapper<>();
            qw.select(SQLEnum.ASTERISK_SIGN.getValue())
                    .from(PetSkill.class)
                    .where("pet_id", item.getId().toString());
            List<Long> skills = petsSkillDao.selectAll(qw).stream().map(PetSkill::getSkillId)
                    .collect(Collectors.toList());
            item.setSkill(querySkillList(skills));
        }
    }

    private List<Skill> querySkillList(List<Long> skills) {
        List<Skill> skillList = new ArrayList<>();
        for (Long skillId : skills) {
            QueryWrapper<Skill> qw = new QueryWrapper<>();
            qw.select(SQLEnum.ASTERISK_SIGN.getValue())
                    .from(Skill.class)
                    .where("id", skillId.toString());
            Skill skill = skillDao.selectOne(qw);
            skillList.add(skill);
        }
        return skillList;
    }
}
